/******************************************************************************/
/*
 *
 * Copyright 2012-2016 AOL Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this Software except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

var https = require('https');
var wiseSource = require('./wiseSource.js');
var util = require('util');

// ----------------------------------------------------------------------------
function OpenDNSSource (api, section) {
  OpenDNSSource.super_.call(this, api, section);
  this.key = this.api.getConfig('opendns', 'key');
  if (this.key === undefined) {
    console.log(this.section, '- No key defined');
    return;
  }

  this.waiting = [];
  this.processing = {};
  this.statuses = { '-1': 'malicious', '0': 'unknown', '1': 'benign' };

  this.api.addSource('opendns', this);
  this.getCategories();
  setInterval(this.getCategories.bind(this), 10 * 60 * 1000);
  setInterval(this.performQuery.bind(this), 500);

  this.statusField = this.api.addField('field:opendns.domain.status;db:opendns.status;kind:lotermfield;friendly:Status;help:OpenDNS domain security status;count:true');
  this.scField = this.api.addField('field:opendns.domain.security;db:opendns.securityCategory;kind:termfield;friendly:Security;help:OpenDNS domain security category;count:true');
  this.ccField = this.api.addField('field:opendns.domain.content;db:opendns.contentCategory;kind:termfield;friendly:Security;help:OpenDNS domain content category;count:true');

  this.api.addView('opendns',
    'if (session.opendns)\n' +
    '  div.sessionDetailMeta.bold OpenDNS\n' +
    '  dl.sessionDetailMeta\n' +
    "    +arrayList(session.opendns, 'status', 'Status', 'opendns.domain.status')\n" +
    "    +arrayList(session.opendns, 'securityCategory', 'Security Cat', 'opendns.domain.security')\n" +
    "    +arrayList(session.opendns, 'contentCategory', 'Content Cat', 'opendns.domain.content')\n"
  );

  this.api.addRightClick('opendnsip', { name: 'OpenDNS', url: 'https://sgraph.opendns.com/ip-view/%TEXT%', category: 'ip' });
  this.api.addRightClick('opendnsasn', { name: 'OpenDNS', url: 'https://sgraph.opendns.com/as-view/%REGEX%', category: 'asn', regex: '^[Aa][Ss](\\d+)' });
  this.api.addRightClick('opendnshost', { name: 'OpenDNS', url: 'https://sgraph.opendns.com/domain-view/name/%HOST%/view', category: 'host' });
}
util.inherits(OpenDNSSource, wiseSource);

// ----------------------------------------------------------------------------
OpenDNSSource.prototype.getCategories = function () {
  var options = {
      host: 'sgraph.api.opendns.com',
      port: '443',
      path: '/domains/categories/',
      method: 'GET',
      headers: {
          'Authorization': 'Bearer ' + this.key
      }
  };

  var response = '';
  var request = https.request(options, (res) => {
    res.on('data', (chunk) => {
      response += chunk;
    });
    res.on('end', () => {
      this.categories = JSON.parse(response);
    });
  });
  request.on('error', (err) => {
    console.log(this.section, err);
  });

  request.end();
};
// ----------------------------------------------------------------------------
OpenDNSSource.prototype.performQuery = function () {
  if (this.waiting.length === 0) {
    return;
  }

  if (this.api.debug > 0) {
    console.log(this.section, '- Fetching %d', this.waiting.length);
  }

  // http://stackoverflow.com/questions/6158933/how-to-make-an-http-post-request-in-node-js/6158966
  // console.log("doing query:", waiting.length, "current cache", Object.keys(cache).length);
  var postData = JSON.stringify(this.waiting);
  this.waiting.length = 0;

  var postOptions = {
      host: 'sgraph.api.opendns.com',
      port: '443',
      path: '/domains/categorization/',
      method: 'POST',
      headers: {
          'Authorization': 'Bearer ' + this.key,
          'Content-Type': 'application/x-www-form-urlencoded',
          'Content-Length': postData.length
      }
  };

  var response = '';
  var request = https.request(postOptions, (res) => {
    res.on('data', (chunk) => {
      response += chunk;
    });
    res.on('end', (err) => {
      var results;
      try {
        results = JSON.parse(response);
      } catch (e) {
        console.log(this.section, 'Error parsing for request:\n', postData, '\nresponse:\n', response);
        results = {};
      }

      for (var result in results) {
        let cbs = this.processing[result];
        if (!cbs) {
          return;
        }
        delete this.processing[result];

        let args = [this.statusField, this.statuses[results[result].status]];

        if (results[result].security_categories) {
          results[result].security_categories.forEach((value) => {
            if (this.categories[value]) {
              args.push(this.scField, this.categories[value]);
            } else {
              console.log(this.section, 'Bad OpenDNS SC', value);
            }
          });
        }

        if (results[result].content_categories) {
          results[result].content_categories.forEach((value) => {
            if (this.categories[value]) {
              args.push(this.ccField, this.categories[value]);
            } else {
              console.log(this.section, 'Bad OpenDNS CC', value, results);
            }
          });
        }

        result = { num: args.length / 2, buffer: wiseSource.encode.apply(null, args) };

        let cb;
        while ((cb = cbs.shift())) {
          cb(null, result);
        }
      }
    });
  });
  request.on('error', (err) => {
    console.log(this.section, err);
  });

  // post the data
  request.write(postData);
  request.end();
};
// ----------------------------------------------------------------------------
OpenDNSSource.prototype.getDomain = function (domain, cb) {
  if (domain in this.processing) {
    this.processing[domain].push(cb);
    return;
  }

  this.processing[domain] = [cb];
  this.waiting.push(domain);
  if (this.waiting.length > 1000) {
    this.performQuery();
  }
};
// ----------------------------------------------------------------------------
exports.initSource = function (api) {
  return new OpenDNSSource(api, 'opendns');
};
// ----------------------------------------------------------------------------
